from jqdata import log

import re
import numpy as np


class SecuritiesInfo():
    """     
    证券信息
    Attributes:
        number_days: # 计算N值的天数
    
    """

    number_days = 20    # 计算N值的天数

    """
    # N值列表
    'Ns': [],

    # 对于股票是标的股票每波动一元，1股股票的总价值变化量
    'dollars_per_share': 1,

    # 价格
    'prices' : pd.DataFrame({})
    """
    data = {}

    
    def __init__(self):
        """  """
        pass

    def sec_is_A(self, security):
        """ 是否A股 """

        sec_type = self.get_sec_type(security)
        return sec_type == 'stock'
    
    def sec_is_future(self, security):
        sec_type = self.get_sec_type(security)
        return sec_type in ['futures', 'index_futures']
    
    def get_sec_type(self, security):
        """证券类型"""

        if security not in self.data.keys():
            log.warning("未知证券")
            return None
        else:
            if 'type' not in self.data[security].keys():
                log.warning("未知证券type信息")
                return None

        return self.data[security]['type']
    
    def sec_minimum_buy_unit(self, security):
        """ 最小买入单元，对于A股是一手多少股 """
        
        if self.sec_is_A(security):
            return 100
        
        if self.sec_is_future(security):
            return 1
        
        raise ValueError("不支持的证券类型")

    
    def sec_is_T1(self, security):
        """证券是否 T+1交易 """

        return self.sec_is_A(security)

    
    def update_Ns(self):
        """
        更新计算所有N
        需要在更新价格之后
        """

        for sec in self.data:
            Ns = []
            if 'Ns' in self.data[sec].keys():
                Ns = self.data[sec]['Ns']
            self.data[sec]['Ns'] = self.calculate_N(Ns, self.data[sec]['prices'])
        pass

    
    def calculate_N(self, Ns, prices):
        """
        # 计算单一证券的N 值
        N 值和价格应当匹配
        # 输入：security 证券代码
        # 输入：prices pd.DataFrame，行情，需要包含： 
        # 输出：N的值的更新列表-list类型
        # 支持三种使用情况:
            Ns = [], len(prices) > number_days
            len(Ns) >= (number_days-1), len(prices) >= 1
            len(Ns) < number_days, 
        """

        number_days = self.number_days
        current_N = 0

        # 之前计算了多少N
        days = len(Ns) + 1

        # 如果交易天数小于等于20天,price 是从开始的
        if days <= number_days:
            #常见初始情况 Ns = [] 
            if len(prices)  > number_days:
                return self.calc_Ns_by_prices(prices)
            else:
                #认为传入的Ns是之前x 天的N结果 截取至相等
                if days > len(prices):
                    prices = prices[-days:]
                else: # 不应
                    pass
                new_Ns = self.calc_Ns_by_prices(prices)
                Ns = Ns.append(new_Ns[-1])

        # 如果交易天数超过20天
        else:
            True_Range = self.calc_true_range(prices[-1:])
            # 计算前g.number_days（大于20）天的True_Range平均值，即当前N的值：
            current_N = (True_Range + (number_days - 1) * Ns[-1]) / number_days
        
        # A list storing info of N
        Ns.append(current_N)
        if len(Ns) > number_days:
            return Ns[-number_days:]
        return Ns
    
    
    def calc_true_range(self, price):
        """
        # 计算 True Range
        # Calculate the True Range
        """

        if len(price) != 1:
            raise Exception("参数异常")
        
        h_l = price['high'][0] - price['low'][0]
        h_c = price['high'][0] - price['pre_close'][0]
        c_l = price['pre_close'][0] - price['low'][0]
        r = max(h_l, h_c, c_l)
        return r

    def calc_Ns_by_prices(self, prices):
        """一次性算好一批 N值"""

        number_days = self.number_days
        prices_len = len(prices)
        lst = []
        for i in range(0, min(prices_len, number_days)):
            True_Range = self.calc_true_range(prices[i:i+1])
            lst.append(True_Range)

        Ns = []
        for i in range(len(lst)):
            Ns.append(np.mean(np.array(lst[:i+1])))

        if prices_len > self.number_days:
            for i in range(prices_len - number_days):
                n = number_days + i
                True_Range = self.calc_true_range(prices[n:n+1])
                current_N = (True_Range + (number_days - 1) * Ns[-1]) / number_days
                Ns.append(current_N)

        if len(Ns) > number_days:
            return Ns[-number_days:]
        return Ns
        
    
    future_code_list = {'A':'A9999.XDCE', 'AG':'AG9999.XSGE', 'AL':'AL9999.XSGE', 'AU':'AU9999.XSGE',
                        'B':'B9999.XDCE', 'BB':'BB9999.XDCE', 'BU':'BU9999.XSGE', 'C':'C9999.XDCE', 
                        'CF':'CF9999.XZCE', 'CS':'CS9999.XDCE', 'CU':'CU9999.XSGE', 'ER':'ER9999.XZCE', 
                        'FB':'FB9999.XDCE', 'FG':'FG9999.XZCE', 'FU':'FU9999.XSGE', 'GN':'GN9999.XZCE', 
                        'HC':'HC9999.XSGE', 'I':'I9999.XDCE', 'IC':'IC9999.CCFX', 'IF':'IF9999.CCFX', 
                        'IH':'IH9999.CCFX', 'J':'J9999.XDCE', 'JD':'JD9999.XDCE', 'JM':'JM9999.XDCE', 
                        'JR':'JR9999.XZCE', 'L':'L9999.XDCE', 'LR':'LR9999.XZCE', 'M':'M9999.XDCE', 
                        'MA':'MA9999.XZCE', 'ME':'ME9999.XZCE', 'NI':'NI9999.XSGE', 'OI':'OI9999.XZCE', 
                        'P':'P9999.XDCE', 'PB':'PB9999.XSGE', 'PM':'PM9999.XZCE', 'PP':'PP9999.XDCE', 
                        'RB':'RB9999.XSGE', 'RI':'RI9999.XZCE', 'RM':'RM9999.XZCE', 'RO':'RO9999.XZCE', 
                        'RS':'RS9999.XZCE', 'RU':'RU9999.XSGE', 'SF':'SF9999.XZCE', 'SM':'SM9999.XZCE', 
                        'SN':'SN9999.XSGE', 'SR':'SR9999.XZCE', 'T':'T9999.CCFX', 'TA':'TA9999.XZCE', 
                        'TC':'TC9999.XZCE', 'TF':'TF9999.CCFX', 'V':'V9999.XDCE', 'WH':'WH9999.XZCE', 
                        'WR':'WR9999.XSGE', 'WS':'WS9999.XZCE', 'WT':'WT9999.XZCE', 'Y':'Y9999.XDCE', 
                        'ZC':'ZC9999.XZCE', 'ZN':'ZN9999.XSGE'}
    def get_future_code(self, index_symbol):
        """
        # 获取当天时间正在交易的期货主力连续代码
        """
        future_code_list = self.future_code_list
        if index_symbol in future_code_list.keys():
            return future_code_list[index_symbol]
        
        log.warning('WARNING: 无此合约')
        return None

    future_coef_list = {'A':10, 'AG':15, 'AL':5, 'AU':1000,
                        'B':10, 'BB':500, 'BU':10, 'C':10, 
                        'CF':5, 'CS':10, 'CU':5, 'ER':10, 
                        'FB':500, 'FG':20, 'FU':50, 'GN':10, 
                        'HC':10, 'I':100, 'IC':200, 'IF':300, 
                        'IH':300, 'J':100, 'JD':5, 'JM':60, 
                        'JR':20, 'L':5, 'LR':10, 'M':10, 
                        'MA':10, 'ME':10, 'NI':1, 'OI':10, 
                        'P':10, 'PB':5, 'PM':50, 'PP':5, 
                        'RB':10, 'RI':20, 'RM':10, 'RO':10, 
                        'RS':10, 'RU':10, 'SF':5, 'SM':5, 
                        'SN':1, 'SR':10, 'T':10000, 'TA':5, 
                        'TC':100, 'TF':10000, 'V':5, 'WH':20, 
                        'WR':10, 'WS':50, 'WT':10, 'Y':10, 
                        'ZC':100, 'ZN':5}
    def get_future_trade_unit(self, symbol):
        """ 期货合约单位 比如1份AG合约相当于15KG """

        if symbol not in self.future_coef_list.keys():
            raise ValueError

        return self.future_coef_list[symbol]


    def get_value_price_multiplier(self, security):
        """单位价格变动导致多少持仓市值变动"""

        if self.sec_is_A(security):
            # 持仓1股价格变化1元
            return 1
        
        if self.sec_is_future(security):
            symbol = re.sub("\d+\.\S+","",security)
            # 1份AG合约15KG，报价为每KG价格，合约价格变化1元，持仓价值变化15元
            return self.get_future_trade_unit(symbol)

    # 期货交易所标准保证金比例
    futures_margin_rate = {
            'A': 0.07,'MA': 0.05,'AG': 0.04,'NI': 0.05,'AL': 0.05,'NR': 0.15,
            'AP': 0.08,'OI': 0.05,'AU': 0.04,'P': 0.05,'B': 0.05,'PB': 0.05,
            'BB': 0.2,'PM': 0.05,'BU': 0.04,'PP': 0.07,'C': 0.07,'RB': 0.05,
            'CF': 0.05,'RI': 0.05,'CJ': 0.07,'RM': 0.05,'CS': 0.07,'RS': 0.05,
            'CU': 0.05,'RU': 0.05,'CY': 0.05,'SC': 0.07,'EG': 0.05,'SF': 0.05,
            'FB': 0.2,'SM': 0.05,'FG': 0.05,'SN': 0.05,'FU': 0.08,'SP': 0.1,
            'HC': 0.04,'SR': 0.05,'I': 0.1,'TA': 0.05,'J': 0.12,'TS': 0.0005,
            'JD': 0.08,'UR': 0.09,'JM': 0.12,'V': 0.07,'JR': 0.05,'WH': 0.05,
            'L': 0.07,'WR': 0.07,'Y': 0.05,'WT': 0.05,'LR': 0.05,'ZC': 0.05,
            'M': 0.07,'ZN': 0.05,
        }
    # TODO: 期货公司实际保证金比例 加点
    futures_margin_rate_plus = {

    }
    def sec_margin_rate(self, security):
        if self.sec_is_A(security):
            # 持仓1股价格变化1元
            return 1
        
        if self.sec_is_future(security):
            symbol = re.sub("\d+\.\S+","",security)
            if symbol in self.futures_margin_rate.keys():
                return self.futures_margin_rate[symbol]

        raise ValueError("不支持的证券")

    
    def is_maincontract(self, security):
        """ 是否主力合约 """

        return self.data[security]['main_contract'] ==  security


    def future_contract2symbol(self, security)->str:
        """ 将期货合约代码转为期货编码 AG9999.xxxx -> AG TODO: 更健壮 """
        symbol = re.sub("\d+\.\S+","",security)
        
        return symbol